/**
 * @file .
 * @author wangxiaowei0303@163.com
 * 若干个可以让 std::string 更加强大,与方便使用的方法.
 */

#include "string_util.h"

#include <stdio.h>
#include <stdlib.h>

#include <stdexcept>

/* 
 * 在函数内首先调用 vsnprintf(0,0,format,ap) 得到格式化后字符串的字节数,然后
 * 在 str 的基础上分配足够的内存;最后再一次调用 vsnprintf() 将格式化后的字符串
 * 写入到 str 的末尾.
 */
std::string& 
vaddFormat(std::string &str,const char *format,va_list ap)
{
    if (format == 0)
        return str;
    /* 
     * 由于此时需要调用2次 vsnprintf(),并且不能保证调用一次之后 ap 不变,
     * 所以这里对 ap 进行了备份.
     */
    va_list ap1;
    va_copy(ap1,ap);
    // need_size,为格式化后的字符串的字节大小.
    int need_size = vsnprintf(0,0,format,ap);
    if (need_size < 0) {
        va_end(ap1);
        throw std::runtime_error("vaddFormat");
    }
    // 若 need_size == 0,则直接返回.
    if (need_size > 0) {
        int old_size = str.size();
        str.resize(old_size + need_size);
        char *buf = (char*)(str.data()+old_size);
        need_size = vsnprintf(buf,need_size+1,format,ap1);
        if(need_size < 0){
            va_end(ap1);
            throw std::runtime_error("vddFormat");
        }
    }
    va_end(ap1);
    return str;
}

std::string& 
addFormat(std::string &str,const char *format,...)
{
    va_list ap;
    va_start(ap,format);
    try{
        vaddFormat(str,format,ap);
    }catch(const std::exception &e){
        va_end(ap);
        throw;
    }
    va_end(ap);
    return str;
}

bool
beginWith(const char *s1,const char *s2,int size1,int size2)
{
    size_t s1_size = strSize(s1,size1);
    size_t s2_size = strSize(s2,size2);
    return s2_size <= s1_size && 
           strncmp(s1,s2,s2_size) == 0;
}

double
str2double(const char *str)
{
    char *badcharptr = 0; /* 指向着 str 中第一个不合法字符 */
    double val = strtod(str,&badcharptr);
    if (badcharptr == str) {/* 此时 str 被认为是不合法的,抛出异常. */
        std::string exceptinfo("str2double(");
        exceptinfo += str;
        exceptinfo += "): fail";
        throw std::runtime_error(exceptinfo);
    }
    return val;     
}
